//
// Created by cmo on 2022/5/13.
//
#include "util.h"

//C/C++代码
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendBasicType(JNIEnv *env, jobject instance, jdouble df, jint k,
        jlong lk, jfloat f, jboolean b, jchar c)
        {
    LOGE("接收Java传递而来的参数：df=%lf,k=%d,lk=%ld",df,k,lk);
    LOGE("接收Java传递而来的参数：f=%f b=%d,c=%c",f ,b,c);
///LOGE("CrazyMoJNI","接收Java传递而来的参数：df=%lf,k=%d,lk=%ld,f=%f b=%d,c=%c",df,k,lk,f ,b,c);//这是个LOG库里的天坑，这样子输出有问题
}

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendString(JNIEnv* env,jobject thiz, jstring temp,jobjectArray strArr)
{
    char* str= const_cast<char *>(env->GetStringUTFChars(temp, 0));
    LOGE("Received Java Data temp=%s:",str);
    int32_t arr_len=env->GetArrayLength(strArr);
    LOGE("Java 传递过来的字符串数组长度为：%d",arr_len);
    for(int i=0;i<arr_len;i++){
        jstring str= static_cast<jstring >(env->GetObjectArrayElement(strArr,i));
        const char* c_str=env->GetStringUTFChars(str,0);
        LOGE("遍历字符串数组：%s",c_str);
        env->ReleaseStringUTFChars(str,c_str);//主动释放局部引用
    }
    env->ReleaseStringUTFChars(temp, str);//主动释放局部引用

}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendBasicRefType(JNIEnv *env, jobject thiz, jobject df,
                                                            jobject k, jobject lk, jobject f,
                                                            jobject b, jobject c) {
    jclass dfclz = env->GetObjectClass(df);
    LOGE("接收Java传递而来的基本类型的封装类型参数：df=%lf,k=%d,lk=%ld",*df, *k, *lk);
    LOGE("接收Java传递而来的基本类型的封装类型参数：f=%f b=%d,c=%c", *f, *b, *c);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendBean(JNIEnv *env, jobject thiz, jobject blog) {
    //1、获取JavaBean 对应的class对象
    jclass beanClz=env->GetObjectClass(blog);
    //2、找到要调用的方法
    // 参数3： 签名
    //getBlogAdrr方法ID
    jmethodID  getAddr = env->GetMethodID(beanClz,"getBlogAdrr","()Ljava/lang/String;");
    //3、调用
    jstring addr= static_cast<jstring>(env->CallObjectMethod(blog, getAddr));
    ///jstring addr=(jstring)(env->CallObjectMethod(bean,getAddr));
    const char* blogAddr = env->GetStringUTFChars(addr, 0);//得到对应的jstring字符串指针，会自动产生局部引用
    LOGE("C++ 调用Java getBlogAdrr方法:%s",blogAddr);

    jmethodID setId=env->GetMethodID(beanClz,"setId","(I)V");
    env->CallVoidMethod(blog,setId,1688);//调用非静态的无返回的方法
    jmethodID getId=env->GetMethodID(beanClz,"getId","()I");
    jint id=env->CallIntMethod(blog,getId);
    LOGE("C++ 调用Java setId更新为:%d",id);

    //调用静态方法，无论是私有方法还是公有方法
    jmethodID showMethod=env->GetStaticMethodID(beanClz,"show","()V");
    env->CallStaticVoidMethod(beanClz,showMethod);

    //在Jni创建java对象：
    jclass newBeanClz = env->FindClass("com/crazymo/jni/bean/Blog");
    //反射创建对象
    //1、获得类的构造方法,方法名固定为<init>,后面的签名为对应构造方法的签名
    jmethodID constuct = env->GetMethodID(newBeanClz,"<init>","(Ljava/lang/String;I)V");
    //创建java字符串
    jstring  newAddr = env->NewStringUTF("在JNI赋值的Addr");
    const char* c_newAddr=env->GetStringUTFChars(newAddr,0);
    LOGE("C++ 调用Java创建BlogAddr字符串:%s",c_newAddr);
    //2、调用构造方法 创建对象
    jobject newBlog=env->NewObject(beanClz,constuct,newAddr,168168);
    jmethodID show2=env->GetMethodID(beanClz,"show2","()V");
    env->CallVoidMethod(newBlog,show2);

    //更新属性
    jfieldID filedId=env->GetFieldID(newBeanClz, "id", "I");
    env->SetIntField(newBlog, filedId,888888);
    jstring newAddr2=env->NewStringUTF("在JNI中更新Addr的值888888");

    jfieldID newAddrField=env->GetFieldID(newBeanClz,"blogAdrr", "Ljava/lang/String;");
    env->SetObjectField(newBlog,newAddrField,newAddr2);

    jint id2=env->GetIntField(newBlog,filedId);
    jstring blogAddr2= static_cast<jstring>(env->GetObjectField(newBlog, newAddrField));
    const char* newAddrStr2=env->GetStringUTFChars(blogAddr2,0);
    LOGE("C++ 获取Java的成员变量id=%d，blogAdrr=%s",id2,newAddrStr2);
    env->CallVoidMethod(newBlog,show2);


    env->ReleaseStringUTFChars(newAddr,c_newAddr);
    env->ReleaseStringUTFChars(addr,blogAddr);
    env->ReleaseStringUTFChars(blogAddr2,newAddrStr2);
    //后面不再使用bean2了 ，我希望它引用对象占用的内存可以被马上回收
    env->DeleteLocalRef(newBlog);
    env->DeleteLocalRef(newAddr2);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendArrays(JNIEnv *env, jobject thiz, jintArray ids,
                                                      jobjectArray blogs) {
    //读取整形数组
    int32_t idLen=env->GetArrayLength(ids);
    LOGE("ID Array's len=%d",idLen);
    if(idLen>0){
        jint* id=env->GetIntArrayElements(ids,NULL);
        for (int i = 0; i < idLen; ++i) {
            LOGE("ids 数组元素%d",*(id+i));
        }
    }
    //读取对象数组
    int32_t blogLen=env->GetArrayLength(blogs);
    LOGE("Blog Array's len=%d",blogLen);
    for (int i = 0; i < blogLen; ++i) {
        jobject blogObj=env->GetObjectArrayElement(blogs,i);
        jclass  blogClz=env->GetObjectClass(blogObj);
        ///jclass blog_clz=env->FindClass("com/crazymo/ndk/jni/JNIHelper");
        jmethodID showMethod = env->GetMethodID(blogClz, "show2", "()V");
        env->CallVoidMethod(blogObj, showMethod);
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendList(JNIEnv *env, jobject thiz, jobject list) {
    jclass listObjClz=env->GetObjectClass(list);
    jclass listClz=env->FindClass("java/util/List");
    //可以写ArrayList，反正是要反射调用get和size函数
    jmethodID get_methd = env->GetMethodID(listObjClz, "get", "(I)Ljava/lang/Object;");
    jmethodID size_methd = env->GetMethodID(listClz, "size", "()I");
    jint list_len = env->CallIntMethod(list, size_methd);
    LOGE("List集合的size：%d", list_len);
    for (int i = 0; i < list_len; i++) {
        jstring str = static_cast<jstring >(env->CallObjectMethod(list, get_methd, i));
        const char *s = env->GetStringUTFChars(str, 0);
        LOGE("List集合的size：%s", s);
        env->ReleaseStringUTFChars(str, s);
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendMap(JNIEnv *env, jobject thiz, jobject map_obj) {
    /**
     * //keySet方式遍历
    Set<Person> set=map.keySet();
    Iterator<Person> it=set.iterator();//得到Set集合的迭代器
    while(it.hasNext()){
        Object key=it.next();//通过迭代器获取键
        String value=map.get(key);//map根据键获取对应的值
        System.out.println(key.toString()+":"+value);
    }
     */
    jclass map_clz = env->GetObjectClass(map_obj);//根据Map的实例获取对应的Class字节码对象
    jmethodID size_map_methd = env->GetMethodID(map_clz, "size", "()I");//获取Map字节码对象反射获取size()方法ID
    jint map_len = env->CallIntMethod(map_obj, size_map_methd);//通过Map实例调用size方法
    LOGE("Map映射的size：%d", map_len);
    jmethodID keyset_method = env->GetMethodID(map_clz, "keySet",
                                               "()Ljava/util/Set;");//获取Map字节码对象获取Set<K> keySet()方法
    jobject keyset_obj = env->CallObjectMethod(map_obj,
                                               keyset_method);//调用keySet方法得到键的Set集合<=>set=map.keySet()
    jclass keyset_clz = env->GetObjectClass(
            keyset_obj);///jclass keyset_clz=env->FindClass("java/util/Set");
    jmethodID iterator_methd = env->GetMethodID(keyset_clz, "iterator", "()Ljava/util/Iterator;");
    jobject iterator_obj = env->CallObjectMethod(keyset_obj, iterator_methd);//等价于调用了set.iterator()
    jclass iterator_clz = env->GetObjectClass(iterator_obj);
    jmethodID hasnext_method = env->GetMethodID(iterator_clz, "hasNext", "()Z");
    jboolean hasnext = env->CallBooleanMethod(iterator_obj, hasnext_method);
    LOGE("C++ Map的容量--hasNext=%d", hasnext);
    while (hasnext) {
        jmethodID next_method = env->GetMethodID(iterator_clz, "next",
                                                 "()Ljava/lang/Object;");//获取Iterator的next方法
        jmethodID getv_methd = env->GetMethodID(map_clz, "get",
                                                "(Ljava/lang/Object;)Ljava/lang/Object;");
        jobject key_obj = env->CallObjectMethod(iterator_obj, next_method);//调用it.next得到Key
        jstring value_obj = static_cast<jstring>(env->CallObjectMethod(map_obj, getv_methd,
                                                                       key_obj));
        const char *c_blog2 = env->GetStringUTFChars(value_obj, 0);
        LOGE("C++ Map遍历的元素：%s", c_blog2);
        hasnext = env->CallBooleanMethod(iterator_obj, hasnext_method);
        env->ReleaseStringUTFChars(value_obj,c_blog2);
    }
    env->DeleteLocalRef(map_clz);
    env->DeleteLocalRef(keyset_obj);
    env->DeleteLocalRef(keyset_clz);
    env->DeleteLocalRef(iterator_obj);
    env->DeleteLocalRef(iterator_clz);

}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_sendJavaObjs(JNIEnv *env, jobject thiz, jint num,
                                                        jstring name, jintArray arry,
                                                        jobjectArray strs) {
    int number=num;
    const char *str_name=env->GetStringUTFChars(name,NULL);
    LOGE("num=%d\tname=%s",number,str_name);
    //用完之后,记得及时进行回收
    env->ReleaseStringUTFChars(name,str_name);
    jint *arry_int= env->GetIntArrayElements(arry,NULL);
    jsize size=env->GetArrayLength(arry);
    if(arry_int!=NULL){
        for (int i = 0; i <size ; ++i) {
            //对于基本类型的可以直接操作指针，修改值
            *(arry_int)+=100;
            LOGE("arry:%d\t",*(arry_int+i));
        }
        //0 代表要刷新，改动native时java层也跟着改变的意思
        env->ReleaseIntArrayElements(arry,arry_int,0);
    }
    //遍历引用类型的数组，不能直接操作指针修改值
    jsize ref_size=env->GetArrayLength(strs);
    jobject jobj=NULL;
    for (int i = 0; i < ref_size; ++i) {
        jobj=env->GetObjectArrayElement(strs,i);
        jstring item= static_cast<jstring>(jobj);
        const char *my_str=env->GetStringUTFChars(item,NULL);
        LOGE("遍历引用型字符串数组：%s\t",my_str);
        env->ReleaseStringUTFChars(item,my_str);
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_jni_ProcessJavaDataHelper_updateUser(JNIEnv *env, jobject thiz, jobject user) {
    const char *usr_clz_name = "com/crazymo/jni/bean/User";
    //1、首先获取对象的字节码
    jclass usr_clz = env->FindClass(usr_clz_name);
    //2、获取对应的methodId
    const char *setName_sig = "(Ljava/lang/String;)V";//可以在Build目录下javac 目录通过javap -s去查看
    jmethodID setName = env->GetMethodID(usr_clz, "setName", setName_sig);
    //3、调用方法
    const char *params = "CrazyMo";
    jstring new_name = env->NewStringUTF(params);
    //TODO 必须注意要传入相对应的类型，此处传入char *类型则会报错，虽然都可以表示字符串
    env->CallVoidMethod(user, setName, new_name);
    LOGE("当前进程：%lu", getpt());
    //!!!!一定要记得尽快回收
    env->DeleteLocalRef(usr_clz);
    env->DeleteLocalRef(user);
    //不能马上进行释放 env->ReleaseStringUTFChars(new_name,params);
}